<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  

  
  <title>Android事件分发机制原理 | 海晨忆的博客</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  
  
  
  <meta name="description" content="推荐：非著名程序员的安卓自定义View教程目录，我的自定义view就是看他的这个系列学会的 &amp;emsp;&amp;emsp;首先，我们需要确认的是android的事件传递机制是责任链模式，如果自己能处理就拦截下来自己处理，如果自己处理不了或者不确定就传递给下一个对象">
<meta property="og:type" content="article">
<meta property="og:title" content="Android事件分发机制原理">
<meta property="og:url" content="https://haichenyi.com/2018/02/22/Android事件分发机制原理/index.html">
<meta property="og:site_name" content="海晨忆的博客">
<meta property="og:description" content="推荐：非著名程序员的安卓自定义View教程目录，我的自定义view就是看他的这个系列学会的 &amp;emsp;&amp;emsp;首先，我们需要确认的是android的事件传递机制是责任链模式，如果自己能处理就拦截下来自己处理，如果自己处理不了或者不确定就传递给下一个对象">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://haichenyi.com/uploads/article/2018-02-22/分发流程.png">
<meta property="og:updated_time" content="2019-09-18T08:59:57.714Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Android事件分发机制原理">
<meta name="twitter:description" content="推荐：非著名程序员的安卓自定义View教程目录，我的自定义view就是看他的这个系列学会的 &amp;emsp;&amp;emsp;首先，我们需要确认的是android的事件传递机制是责任链模式，如果自己能处理就拦截下来自己处理，如果自己处理不了或者不确定就传递给下一个对象">
<meta name="twitter:image" content="https://haichenyi.com/uploads/article/2018-02-22/分发流程.png">
  
    <link rel="alternate" href="/atom.xml" title="海晨忆的博客" type="application/atom+xml">
  
  
    <link rel="icon" href="/uploads/artistic_image/head.jpg">
  
  
    <link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
  
  <link rel="stylesheet" href="/css/style.css">
  <link rel="stylesheet" href="/css/highlight.css">
  <script>
  let antiquityStorage = window.sessionStorage.getItem('antiquitySessionStorage');
  if (antiquityStorage == '' || antiquityStorage == null) {
    var antiquityLoader = '<div id="loaderbox"><div class="loader"><div class="load-roll"><div class="load-top"></div><div class="load-right"></div><div class="load-bottom"></div></div></div></div>';
    document.write(antiquityLoader);
    document.body.style.overflow = 'hidden'
  }
  </script>
</head>

<body>
  <div id="fullpage" class="mobile-nav-right">
    
      <div id="wrapper" style="background-image: url(/uploads/artistic_image/bg.jpg)" title="背景图片来自网络">
    
    
      <header id="header">
  <div id="nav-toggle" class="nav-toggle"></div>
  <div class="head-box global-width">
    <nav class="nav-box nav-right">
      
        <a class="nav-item" href="/" title
        
        >首页</a>
      
        <a class="nav-item" href="/archives" title
        
        >归档</a>
      
        <a class="nav-item" href="/about" title
        
        >简历</a>
      
    </nav>
  </div>
</header>
      <div id="middlecontent" title class="global-width sidebar-left">
        <section id="main"><article id="post-Android事件分发机制原理" class="article global-container article-type-post" itemscope itemprop="blogPost">
  
    <header class="article-header">
      
  
    <h1 class="article-title" itemprop="name">
      Android事件分发机制原理
    </h1>
  

    </header>
  
  <div class="article-meta">
    <a href="/2018/02/22/Android事件分发机制原理/" class="article-date">
  <time datetime="2018-02-22T13:30:11.000Z" itemprop="datePublished">2018-02-22</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/Android-自定义view/">Android -自定义view</a>
  </div>

    
  </div>
  
    <span id="busuanzi_container_page_pv">
      本文总阅读量<span id="busuanzi_value_page_pv"></span>次
    </span>
  

  <div class="article-inner">
    
    <div class="article-content article-content-cloud doorframe mac" itemprop="articleBody">
      
        <p>推荐：<a href="http://www.gcssloop.com/#blog" target="_blank" rel="noopener">非著名程序员</a>的<a href="http://www.gcssloop.com/customview/CustomViewIndex" target="_blank" rel="noopener">安卓自定义View教程目录</a>，我的自定义view就是看他的这个系列学会的</p>
<p>&emsp;&emsp;首先，我们需要确认的是android的事件传递机制是<strong>责任链模式</strong>，如果自己能处理就拦截下来自己处理，如果自己处理不了或者不确定就传递给下一个对象</p>
<a id="more"></a>
<h2 id="事件的拦截、分发、消费"><a href="#事件的拦截、分发、消费" class="headerlink" title="事件的拦截、分发、消费"></a>事件的拦截、分发、消费</h2><p>&emsp;&emsp;我们要知道三个方法，控制事件反而拦截，分发与消费。如下表：</p>
<table>
<thead>
<tr>
<th style="text-align:center">类型</th>
<th style="text-align:center">相关方法</th>
<th style="text-align:center">Activity</th>
<th style="text-align:center">ViewGroup</th>
<th style="text-align:center">View</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">事件分发</td>
<td style="text-align:center">dispatchTouchEvent</td>
<td style="text-align:center">有</td>
<td style="text-align:center">有</td>
<td style="text-align:center">无</td>
</tr>
<tr>
<td style="text-align:center">事件拦截</td>
<td style="text-align:center">onInterceptTouchEvent</td>
<td style="text-align:center">无</td>
<td style="text-align:center">有</td>
<td style="text-align:center">无</td>
</tr>
<tr>
<td style="text-align:center">事件处理</td>
<td style="text-align:center">onTouchEvent</td>
<td style="text-align:center">有</td>
<td style="text-align:center">有</td>
<td style="text-align:center">有</td>
</tr>
</tbody>
</table>
<p>以上三个方法，均有boolean类型的返回值，通过设置true，false来控制事件传递的流程</p>
<p>Activity和View均没有事件拦截方法，是因为</p>
<ol>
<li><p>Activity作为事件响应起点，如果，Activity把事件拦截了，辣么将为导致整个屏幕都无法点击</p>
</li>
<li><p>View作为事件响应的最末端，要么消费事件，要么不处理回传，没必要拦截事件</p>
</li>
</ol>
<h2 id="事件分发流程"><a href="#事件分发流程" class="headerlink" title="事件分发流程"></a>事件分发流程</h2><p>&emsp;&emsp;先给出一张图<img src="/uploads/article/2018-02-22/分发流程.png" alt="分发流程.jpg"></p>
<p>&emsp;&emsp;这里，我们看到多了两个东西，一个是PhoneWindow，一个是DecorView，我们平时写的布局，最外层的父布局就是这里的RootView，有过几个app开发经验的程序员都应该知道，app的状态栏颜色的修改，还有那个主题的内容修改，修改后显示在哪呢？没错，就是DecorView里面。</p>
<p>&emsp;&emsp;再有就是phoneWindow，其实，知不知道没什么用，phonewindow是Window的唯一实现类，Window是干嘛的呢？Window是一个抽象类，所有的视图，事件传递都归它管理，所以，phonewindow管理视图，上面说的DecorView就是phonewindow的一个内部实现类，除了自己的功能外，还负责消息传递。</p>
<p>&emsp;&emsp;说了这么多，该来讲讲事件传递的流程了。android的view是树形结构的，基于这样的结构，我们的事件可以有序的分发。事件收集之后，起点是Activity，然后有序的向下传递，大致如下：</p>
<p><code>Activity －&gt; PhoneWindow －&gt; DecorView －&gt; ViewGroup －&gt; ... －&gt; View</code></p>
<p>如果这个事件没有对象处理，辣么，它会依次往回传递，如果还是没有人处理，辣么就会被Activity抛弃掉</p>
<p><code>Activity &lt;－ PhoneWindow &lt;－ DecorView &lt;－ ViewGroup &lt;－ ... &lt;－ View</code></p>
<p>这就是文章开头我们提到的，责任链模式</p>
<p>下面我给出ViewGroup的事件分发机制的伪代码：</p>
<div class="highlight-box"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="PLAIN"><figure class="iseeu highlight /plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">public boolean dispatchTouchEvent(MotionEvent ev) &#123;</span><br><span class="line">    boolean result = false;             // 默认状态为没有消费过</span><br><span class="line"></span><br><span class="line">    if (!onInterceptTouchEvent(ev)) &#123;   // 如果没有拦截交给子View</span><br><span class="line">        result = child.dispatchTouchEvent(ev);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    if (!result) &#123;                      // 如果事件没有被消费,询问自身onTouchEvent</span><br><span class="line">        result = onTouchEvent(ev);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    return result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>&emsp;&emsp;情景：老板: 我看公司最近业务不咋地，准备发展一下电商业务，下周之前做个淘宝出来试试怎么样。<br>&emsp;&emsp;事件顺序，老板(MainActivity)要做淘宝，这个事件通过各个部门(ViewGroup)一层一层的往下传，传到最底层的时候，码农小王(View1)发现做不了，于是消息又一层一层的回传到老板那里。<br>&emsp;&emsp;可以看到整个事件传递路线非常有序。从Activity开始，最后回传给Activity结束(由于我们无法操作Phone Window和DecorView，所以没有它们的信息)。</p>
<div class="highlight-box"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="PLAIN"><figure class="iseeu highlight /plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">MainActivity [老板]: dispatchTouchEvent     经理,我准备发展一下电商业务,下周之前做一个淘宝出来.</span><br><span class="line">RootView     [经理]: dispatchTouchEvent     呼叫技术部,老板要做淘宝,下周上线.</span><br><span class="line">RootView     [经理]: onInterceptTouchEvent  (老板可能疯了,但又不是我做.)</span><br><span class="line">ViewGroupA   [组长]: dispatchTouchEvent     老板要做淘宝,下周上线?</span><br><span class="line">ViewGroupA   [组长]: onInterceptTouchEvent  (看着不太靠谱,先问问小王怎么看)</span><br><span class="line">View1        [码农]: dispatchTouchEvent     做淘宝???</span><br><span class="line">View1        [码农]: onTouchEvent           这个真心做不了啊.</span><br><span class="line">ViewGroupA   [组长]: onTouchEvent           小王说做不了.</span><br><span class="line">RootView     [经理]: onTouchEvent           报告老板, 技术部说做不了.</span><br><span class="line">MainActivity [老板]: onTouchEvent           这么简单都做不了,你们都是干啥的(愤怒).</span><br></pre></td></tr></table></figure></div>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>事件分发机制设计到到情形非常多，这里就不一一列举了，记住以下几条原则就行了。</p>
<ol>
<li><p>如果事件被消费，就意味着事件信息传递终止。</p>
</li>
<li><p>如果事件一直没有被消费，最后会传给Activity，如果Activity也不需要就被抛弃。</p>
</li>
<li><p>判断事件是否被消费是根据返回值，而不是根据你是否使用了事件。</p>
</li>
</ol>

      
    </div>
    
      <footer class="article-footer">
        完
      </footer>
    
  </div>
  
    
<nav id="article-nav">
  <div class="article-nav-block">
    
      <a href="/2018/02/22/MVC、MVP、MVVM比较/" id="article-nav-newer" class="article-nav-link-wrap">
        <strong class="article-nav-caption"></strong>
        <div class="article-nav-title">
          
            MVC、MVP、MVVM比较
          
        </div>
      </a>
    
  </div>
  <div class="article-nav-block">
    
      <a href="/2018/02/08/Android自定义View/" id="article-nav-older" class="article-nav-link-wrap">
        <div class="article-nav-title">Android自定义View</div>
        <strong class="article-nav-caption"></strong>
      </a>
    
  </div>
</nav>

    
  
  
</article>
</section>
        <aside id="sidebar">
  
    <div class="widget-box">
  <div class="avatar-box avatar-item">
    <img class="avatar" src="/uploads/artistic_image/head.jpg" title="头像来自网络"></img>
    <h3 class="avatar-name">
      
        海晨忆
      
    </h3>
    <p class="avatar-slogan">
      先谋生，再谋爱。人间值得，未来可期。
    </p>
  </div>
</div>


  
    
  <div class="widget-box">
    <h3 class="widget-title">分类</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/Android-Socket/">Android -Socket</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/Android-加密算法/">Android -加密算法</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/Android-常用功能/">Android -常用功能</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/Android-框架/">Android -框架</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/Android-源码解析/">Android -源码解析</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/Android-自定义view/">Android -自定义view</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/Android-设计模式/">Android -设计模式</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/JAVA-并发/">JAVA -并发</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/Java-SpringBoot/">Java -SpringBoot</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/Tomcat/">Tomcat</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/kotlin/">kotlin</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/数据库-JDBC/">数据库 -JDBC</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/数据库-MySQL/">数据库 -MySQL</a></li></ul>
    </div>
  </div>


  
    

  
    
  
    
  <div class="widget-box">
    <h3 class="widget-title">归档</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/07/">July 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/11/">November 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/10/">October 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/09/">September 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/08/">August 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/07/">July 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/06/">June 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/10/">October 2018</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/05/">May 2018</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/03/">March 2018</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/02/">February 2018</a></li></ul>
    </div>
  </div>

  
    
  <div class="widget-box">
    <h3 class="widget-title">最新文章</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/2021/07/27/JAVA—公平锁，非公平锁，悲观锁，乐观锁，死锁/">JAVA—公平锁，非公平锁，悲观锁，乐观锁，死锁</a>
          </li>
        
          <li>
            <a href="/2021/07/19/JAVA—线程同步器AQS/">JAVA—线程同步器AQS</a>
          </li>
        
          <li>
            <a href="/2021/07/07/Android模拟点击/">Android模拟点击</a>
          </li>
        
          <li>
            <a href="/2019/11/27/SpringBoot系列-消息-RabbitMQ-（二十）/">SpringBoot系列-消息(RabbitMQ)（二十）</a>
          </li>
        
          <li>
            <a href="/2019/11/13/SpringBoot系列-Docker（十九）/">SpringBoot系列-Docker（十九）</a>
          </li>
        
      </ul>
    </div>
  </div>

  
      <div class="widget-box">
    <h3 class="widget-title">友情链接</h3>
    <div class="widget">
      
        <a class="hrf" style="display: block;" href="https://github.com/haichenyi" title target='_blank'
        >Github</a>
      
        <a class="hrf" style="display: block;" href="https://www.jianshu.com/u/6077ee440c37" title target='_blank'
        >简书</a>
      
        <a class="hrf" style="display: block;" href="https://blog.csdn.net/qq_27634797" title target='_blank'
        >CSDN</a>
      
    </div>
  </div>

  
</aside>
      </div>
      <footer id="footer">
  <div class="foot-box footers global-width">
    &copy;2017-2021 海晨忆 &nbsp;&nbsp;
    <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    <span id="busuanzi_container_site_pv">阁下是第<span id="busuanzi_value_site_pv"></span>个访客</span>
  </div>
</footer>
      <script src="https://code.jquery.com/jquery-2.0.3.min.js"></script>
<script>
if (!window.jQuery) {
var script = document.createElement('script');
script.src = "/js/jquery-2.0.3.min.js";
document.body.write(script);
}
</script>

  <link rel="stylesheet" href="/fancybox/jquery.fancybox.css">
  <script src="/fancybox/jquery.fancybox.pack.js"></script>


<script src="/js/script.js"></script>



    </div>
    <nav id="mobile-nav" class="mobile-nav-box">
  <div class="mobile-nav-img mobile-nav-top"></div>
  
    <a href="/" class="mobile-nav-link">首页</a>
  
    <a href="/archives" class="mobile-nav-link">归档</a>
  
    <a href="/about" class="mobile-nav-link">简历</a>
  
  <div class="mobile-nav-img  mobile-nav-bottom"></div>
</nav>    
  </div>
</body>
</html>